/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#if !defined(___GRAPH2D_DESKTOP_H___)
#define ___GRAPH2D_DESKTOP_H___

#include "color.h"
#include "vector2.h"

#include "abort.h"
#include "data.h"
#include "stopwatch.h"

#include "component.h"
#include "container.h"
#include "scene.h"

#include "soundPlayer.h"

#include <libmana.h>
#include <cstdarg>
#include <string>

namespace Graph2D
{
	class AudioPlayer;
	namespace Sound
	{
		class DataManager;
		class Player;
		class MusicPlayer;
	}

	// TODO:セーブとロード機能

	typedef enum SoundPlayerID
	{
		SOUND_PLAYER_ID_BGM = 0,
		SOUND_PLAYER_ID_SE,
		SOUND_PLAYER_ID_SYSTEM,
		SOUND_PLAYER_ID_SIZE
	}SoundPlayerID;

	typedef struct DebugMessageInfomation
	{
		int x;
		int y;
		std::string string;
		DebugMessageInfomation* next;
	}DebugMessageInfomation;

	class Desktop
	{
	public:
		static void initialize()
		{
			if(instance == NULL)
			{
				instance = new Desktop();
			}
		}

		static void finalize()
		{
			if(instance)
			{
				instance = NULL;
			}
		}

		static Desktop& getInstance()
		{
			return *instance;
		}

		Desktop();

		void initializeResources();

		Component* find(const unsigned int identification) const
		{
			return Component::find(identification);
		}

		const Vector2& getSize() const
		{
			return GraphicDevice::getScreenSize();
		}

		bool rangeOfVisibility(const Vector2& lt, const Vector2& rb) const
		{
			const Vector2& size = GraphicDevice::getScreenSize();
			return lt.x < size.x && rb.x >= 0 && lt.y < size.y && rb.y >= 0;
		}

		Scene& getScene() const								//!< 現在のシーンを取得する
		{
			checkScene();
			return *scene;
		}

		void setScene(Scene* scene)						//!< シーンを設定する
		{
			reserveScene = scene;
		}

		Scene* getPrepareScene() const						//!< 現在準備中のシーンを取得する
		{
			return prepareScene;
		}

		void setPrepareScene(Scene* scene)					//!< 準備中のシーンを設定する
		{
			prepareScene = scene;
		}

		void swapScene(const bool releaseBackScene)
		{
			Scene* temporary = scene;
			scene = backScene;
			backScene = temporary;
			this->releaseBackScene = releaseBackScene;
		}

		Scene* getBackScene() const							//!< 裏のシーンを取得する
		{
			return backScene;
		}

		const Color& getFadeColor() const
		{
			return fadeColor;
		}

		void setFadeColor(const Color& color, const float second);

		bool loadScript(const std::string& filename)
		{
			return scene ? scene->loadScript(filename) : false;
		}

		void unloadScript()
		{
			if(scene)
				scene->unloadScript();
		}

		mana* getScript() const
		{
			return scene ? scene->getScript() : NULL;
		}

		bool getUserControl() const
		{
			return userControl;
		}

		void setUserControl(const bool userControl)
		{
			this->userControl = userControl;
		}

		static float getMinimumTime()
		{
			return 2.0f / 60.0f;
		}

		static float getMostDelayTime()
		{
#if defined(DEBUG) || defined(_DEBUG) || defined(_DEBUG_) || !defined(NDEBUG)
			return 2.0f / 60.0f;
#else
			return 8.0f / 60.0f;
#endif
		}

		Sound::DataManager* getSoundDataManager(const SoundPlayerID index)
		{
			return dataManager[index];
		}

		void setSoundDataManager(const SoundPlayerID index, Sound::DataManager* dataManager)
		{
			this->dataManager[index] = dataManager;
		}

		Sound::Player* getSoundPlayer()
		{
			return player;
		}

		void setSoundPlayer(Sound::Player* player)
		{
			this->player = player;
		}

		Sound::MusicPlayer* getMusicPlayer()
		{
			return musicPlayer;
		}

		void setMusicPlayer(Sound::MusicPlayer* musicPlayer)
		{
			this->musicPlayer = musicPlayer;
		}

		void serialize(mana_stream* stream) const;

		void requestDeserialize(const std::string& filename)
		{
			requestDeserializeFileName = filename;
		}

		void updateAndDraw(const float elapsedSeconds);

		void touchesBegan(const Vector2& position);
		void touchesMoved(const Vector2& position);
		void touchesEnded(const Vector2& position);
		void touchesCancelled(const Vector2& position);

#if defined(DEBUG) || defined(_DEBUG) || defined(_DEBUG_) || !defined(NDEBUG)
		void print(const int x, const int y, const char* format, ...);
#endif

	protected:
		virtual ~Desktop();

	private:
		void changeScene();								//!< シーン換えの処理

		void checkScene() const
		{
			if(scene == NULL)
			{
				abort(ERROR_CODE_ILLEGAL_SCENE_OBJECT_DETECTED);
			}
		}

		Component* deserialize(mana_stream* stream) const;

#if defined(DEBUG) || defined(_DEBUG) || defined(_DEBUG_) || !defined(NDEBUG)
		void cleanDebugMessage();
		void drawDebugMessage();
		void addDebugMessage(const int x, const int y, const std::string& message);
#endif

	private:
		Scene defaultScene;										//!< デフォルトシーン

		Scene* scene;											//!< 現在のシーンのポインタ
		Scene* reserveScene;									//!< 予約されたシーンのポインタ
		Scene* prepareScene;									//!< 現在準備中のシーン
		Scene* backScene;										//!< のシーン

		Color fadeColor;										//!< フェードカレントカラー
		Color fadeDeltaColor;									//!< フェードカラー差分
		Color fadeTargetColor;									//!< フェードターゲットカラー
		float fadeColorTime;									//!< フェード時間
		bool fadeColorEnable;									//!< フェードﾕｳｺｳフラグ

		Stopwatch lastUpdateTime;								//!< 最終更新時間

		Sound::DataManager* dataManager[SOUND_PLAYER_ID_SIZE];	//
		Sound::Player* player;
		Sound::MusicPlayer* musicPlayer;

		std::string requestDeserializeFileName;

		bool userControl;
		bool releaseBackScene;

#if defined(DEBUG) || defined(_DEBUG) || defined(_DEBUG_) || !defined(NDEBUG)
		DebugMessageInfomation* debugMessageInfomation;
		Texture* debugMessageFontTexture;
		Stopwatch lastIntervalTime;
#endif

		static Desktop* instance;
	};
}

#if defined(DEBUG) || defined(_DEBUG) || defined(_DEBUG_) || !defined(NDEBUG)
#define GRAPH2D_TRACE(x, y, m)	if(0)Graph2D::print
#else
#define GRAPH2D_TRACE(x, y, m)	Graph2D::print
#endif

#endif
